//
//  panManager.m
//  Void Dead
//
//  Created by Sidney Just on 04.07.09.
//  Copyright 2009 by Sidney Just
//  This program is free software; you can redistribute it and/or modify it under the terms of the GNU General Public License as published by the Free Software Foundation; 
//  either version 3 of the License, or (at your option) any later version.
//  
//  This program is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty 
//  of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for more details.
//  

#import "panelManager.h"
#import "TextureManager.h"
#import "panel.h"
#import "Engine.h"
#import "Animation.h"
#import "ParticleEmitter.h"
#import "Particle.h"

@implementation VD_PanelManager
@synthesize touchWasIn, panelCamera_x, panelCamera_y, aktPanels, touchSelectMode, alphaThreshold;

#pragma mark -
#pragma mark init dealloc

- (id)initWithEngine:(VD_Engine *)eng {
	if(self = [super init])
	{
		panelCamera_x = 0;
		panelCamera_y = 0;
		render_view = eng;
		texMan = eng.texManager;
		emptyPanels = 0;
		touchSelectMode = kDefaultMode;
		alphaThreshold = 8;
		
		emitterCount = 0;
	}
	return self;
}

- (void)dealloc {
	for(int i=0; i<emitterCount; i++)
	{
		[emitter[i] release];
	}
	free(emitter);
	
	for(int i=0; i<aktPanels; i++)
	{
		[panels[i] release];
	}
	free(panels);
	
	for(int i=0; i<aktAnimations; i++)
	{
		[animations[i] release];
	}
	free(animations);
	
	[super dealloc];
}

#pragma mark -
#pragma mark functions

- (VD_ParticleEmitter *)createParticleEmitter {
	emitterCount += 1;
	emitter = (VD_ParticleEmitter **)realloc(emitter, emitterCount*sizeof(VD_ParticleEmitter *));
	
	emitter[emitterCount-1] = [[VD_ParticleEmitter alloc] initWithEngine:render_view];
	return emitter[emitterCount-1];
}

- (VD_Panel *)createPanel:(int)layer {

	aktPanels += 1;
	panels = (VD_Panel **)realloc(panels, aktPanels*sizeof(VD_Panel *));
	
	int i;
	
	for(i=0; i<aktPanels-1; i++)
	{
		if(panels[i].layer == layer || panels[i].layer > layer)
		{
			for(int j=aktPanels-1; j>i; j--)
			{
				panels[j] = panels[j-1];
			}
			
			panels[i] = [[VD_Panel alloc] initWithEngine:render_view];
			panels[i].layer = layer;
			
			return panels[i];
		}
	}
	
	panels[aktPanels-1] = [[VD_Panel alloc] initWithEngine:render_view];
	panels[aktPanels-1].layer = layer;
	return panels[aktPanels-1];
}

- (VD_Animation *)createAnimation:(VD_Panel *)pan withStart:(float)startPoint End:(float)endPoint Speed:(float)speed Size:(float)tileSize andLeap:(float)leap 
{
	aktAnimations += 1;
	animations = (VD_Animation **)realloc(animations, aktAnimations*sizeof(VD_Animation *));
	int i = aktAnimations-1;
	
	animations[i] = [[VD_Animation alloc] init];
	
	float texscale = texMan.size[pan.texture].x;
	
	if(startPoint == -1)
	{
		animations[i].animationStart = pan.atlasPos_x;
	} else {
		animations[i].animationStart = (startPoint / texscale);
	}
	
	animations[i].animationEnd = (endPoint / texscale);
	animations[i].animationSpeed = speed;
	
	if(tileSize == -1)
	{
		animations[i].animationTileSize = pan.atlasSize_x - pan.atlasPos_x;
		animations[i].animationLeap = (leap * (pan.atlasSize_x - pan.atlasPos_x));
	} else {
		animations[i].animationTileSize = (tileSize / texscale);
		animations[i].animationLeap = ((leap * tileSize) / texscale);
	}
	
	animations[i].atlasPos = animations[i].animationStart;
	animations[i].atlasSize = animations[i].animationStart + animations[i].animationTileSize;
	
	pan.animated = YES;
	pan.animation = animations[i];
	
	return animations[i];
}

- (void)updatePanel:(VD_Panel *)pan withAtlas:(BOOL)at forAnimation:(VD_Animation *)anim {
	if(pan.layer < lastLayer)
	{
	} else {
		lastLayer = pan.layer;
	}
	
	if(at == YES)
	{
		pan.atlasSize_x = pan.atlasPos_x + pan.atlasSize_x;
		pan.atlasSize_y = pan.atlasPos_y + pan.atlasSize_y;
		
		pan.size.x = (pan.atlasPos_x - pan.atlasSize_x) * pan.scale.x;
		pan.size.y = (pan.atlasPos_y - pan.atlasSize_y) * pan.scale.y;
		
		if(pan.size.x < 0)
			pan.size.x = pan.size.x*(-1);
		
		if(pan.size.y < 0)
			pan.size.y = pan.size.y*(-1);
		
		if(pan.atlasPos_x > 0)
			pan.atlasPos_x = pan.atlasPos_x / texMan.size[pan.texture].x;
		
		if(pan.atlasPos_y > 0)
			pan.atlasPos_y = pan.atlasPos_y / texMan.size[pan.texture].y;
		
		if(pan.atlasSize_x > 0)
			pan.atlasSize_x = pan.atlasSize_x / texMan.size[pan.texture].x;
		
		if(pan.atlasSize_y > 0)
			pan.atlasSize_y = pan.atlasSize_y / texMan.size[pan.texture].y;
		
		pan.animated = NO;
	} else {
		pan.size.x = texMan.size[pan.texture].x * pan.scale.x;
		pan.size.y = texMan.size[pan.texture].y * pan.scale.y;
	}
	
	if(anim != NULL)
	{
		pan.animation = anim;
	}
}

- (void)createDefaultShape:(VD_Panel *)pan {
	pan.animated = NO;
	
	pan.atlasPos_x = 0;
	pan.atlasPos_y = 0;
	
	pan.atlasSize_x = texMan.size[pan.texture].x;
	pan.atlasSize_y = texMan.size[pan.texture].y;
	
	pan.atlasSize_x = pan.atlasPos_x + pan.atlasSize_x;
	pan.atlasSize_y = pan.atlasPos_y + pan.atlasSize_y;
	
	pan.size.x = (pan.atlasPos_x - pan.atlasSize_x) * pan.scale.x;
	pan.size.y = (pan.atlasPos_y - pan.atlasSize_y) * pan.scale.y;
	
	if(pan.size.x < 0)
		pan.size.x = pan.size.x*(-1);
	
	if(pan.size.y < 0)
		pan.size.y = pan.size.y*(-1);
	
	if(pan.atlasPos_x > 0)
		pan.atlasPos_x = pan.atlasPos_x / texMan.size[pan.texture].x;
	
	if(pan.atlasPos_y > 0)
		pan.atlasPos_y = pan.atlasPos_y / texMan.size[pan.texture].y;
	
	if(pan.atlasSize_x > 0)
		pan.atlasSize_x = pan.atlasSize_x / texMan.size[pan.texture].x;
	
	if(pan.atlasSize_y > 0)
		pan.atlasSize_y = pan.atlasSize_y / texMan.size[pan.texture].y;
}

- (void)drawAll {	
	VD_Panel *pan;
	VD_Panel *lastPan = NULL;
	
	polyCount = 0;
	drawCalls = 0;
	drawVertices = 0;
	
	for(int i=0; i<emitterCount; i++)
	{
		emitter[i].pos_x += panelCamera_x;
		emitter[i].pos_y += panelCamera_y;
		
		[emitter[i] spawnParticles];
	}
	
	glMatrixMode(GL_PROJECTION);
	glLoadIdentity();
	glOrthof(0, render_view.backingWidth, 0, render_view.backingHeight, -1, 1);	
	
	glMatrixMode(GL_MODELVIEW);
	glEnableClientState(GL_VERTEX_ARRAY);
	glEnableClientState(GL_TEXTURE_COORD_ARRAY);
	
	
	//Preprocess Animation
	for(int i=0; i<aktAnimations; i++)
	{
		animations[i].animationStep += render_view.timeStep;
		
		if(animations[i].animationStep >= animations[i].animationSpeed)
		{
			animations[i].animationStep = 0;
			
			animations[i].atlasPos += animations[i].animationLeap;
			animations[i].atlasSize = animations[i].atlasPos + animations[i].animationTileSize;
			
			if(animations[i].atlasPos >= animations[i].animationEnd)
			{
				animations[i].atlasPos = animations[i].animationStart;
				animations[i].atlasSize = animations[i].animationStart + animations[i].animationTileSize;
			}
			
		}
	}
	
	id tempId;
	VD_ParticleEmitter *tempEmitter;
	
	if(aktPanels > 0)
	{
		int panCount = 0;
		int vertices = 0;
		int coords = 0;
		
		VD_Panel **visPans = (VD_Panel **)malloc(aktPanels*sizeof(VD_Panel *));
		
		int i = 0;
		
		for(i=0; i<aktPanels; i++)
		{

			if(panels[i].changeByCam == YES)
			{
				if(panels[i].parallaxScrolled == NO)
				{
					panels[i].pos_x += panelCamera_x;
					panels[i].pos_y += panelCamera_y;
				} else {
					panels[i].pos_x += panelCamera_x*panels[i].layer;
					panels[i].pos_y += panelCamera_y*panels[i].layer;
				}
			}
			
			
			 
			if([panels[i] isInScreenBounds] && panels[i].visible == YES)
			{
				visPans[panCount] = panels[i];
				panCount += 1;
				
				polyCount += 2;
				drawVertices += 12;
			}
			
		}
		
		GLfloat *panVertices = (GLfloat *)malloc((panCount*12)*sizeof(GLfloat));
		GLfloat *panTexCoords = (GLfloat *)malloc((panCount*12)*sizeof(GLfloat));
		
		lastTexHadAlpha = NO;
		
		for(i=0; i<panCount; i++)
		{
			pan = visPans[i];
			
			if(lastPan != NULL)
			{
				if(lastPan.texture != pan.texture)
				{
					drawCalls += 1;
					
					tempId = lastPan.userData;
					
					if([tempId isKindOfClass:[VD_ParticleEmitter class]])
					{	
						tempEmitter = tempId;
						
						if(tempEmitter.bright == YES)
							glBlendFunc(GL_SRC_ALPHA, GL_DST_ALPHA);
					}
					
					glBindTexture(GL_TEXTURE_2D, texMan.texLib[lastPan.texture]);
					
					glLoadIdentity();
					
					glVertexPointer(2, GL_FLOAT, 0, panVertices);
					glTexCoordPointer(2, GL_FLOAT, 0, panTexCoords);
					
					glDrawArrays(GL_TRIANGLES, 0, vertices/2);
					
					vertices = 0;
					coords = 0;
					
					if([tempId isKindOfClass:[VD_ParticleEmitter class]])
					{
						if(tempEmitter.bright == YES)
							glBlendFunc(GL_SRC_ALPHA, GL_ONE_MINUS_SRC_ALPHA);
					}
				}
			}
			
			lastPan = pan;
			
			vertices += 12;
			coords += 12;
			
			if(panels[i].animated == YES)
			{
				panTexCoords[coords-12] = pan.animation.atlasPos;
				panTexCoords[coords-11] = pan.atlasSize_y;
				
				panTexCoords[coords-10] = pan.animation.atlasSize;
				panTexCoords[coords-9] = pan.atlasSize_y;
				
				panTexCoords[coords-8] = pan.animation.atlasPos;
				panTexCoords[coords-7] = pan.atlasPos_y;
				
				panTexCoords[coords-6] = pan.animation.atlasSize;
				panTexCoords[coords-5] = pan.atlasPos_y;
				
				panTexCoords[coords-4] = pan.animation.atlasSize;
				panTexCoords[coords-3] = pan.atlasSize_y;
				
				panTexCoords[coords-2] = pan.animation.atlasPos;
				panTexCoords[coords-1] = pan.atlasPos_y;
				
			} else {
				
				panTexCoords[coords-12] = pan.animation.atlasPos;
				panTexCoords[coords-11] = pan.atlasSize_y;

				panTexCoords[coords-10] = pan.atlasSize_x;
				panTexCoords[coords-9] = pan.atlasSize_y;
				
				panTexCoords[coords-8] = pan.atlasPos_x;
				panTexCoords[coords-7] = pan.atlasPos_y;
				
				panTexCoords[coords-6] = pan.atlasSize_x;
				panTexCoords[coords-5] = pan.atlasPos_y;
				
				panTexCoords[coords-4] = pan.atlasSize_x;
				panTexCoords[coords-3] = pan.atlasSize_y;
				
				panTexCoords[coords-2] = pan.atlasPos_x;
				panTexCoords[coords-1] = pan.atlasPos_y;
			}
			
			float vert_temppos_x;
			float vert_temppos_y;

			float vert_dist;
			float vert_ang;
			
			if(pan.rotation == 0)
			 {
				 //vertices
				 panVertices[vertices-12] = pan.pos_x;
				 panVertices[vertices-11] = render_view.backingHeight-pan.pos_y-pan.size.y;
				 
				 panVertices[vertices-10] = pan.pos_x+pan.size.x;
				 panVertices[vertices-9] = render_view.backingHeight-pan.pos_y-pan.size.y;
				 
				 panVertices[vertices-8] = pan.pos_x;
				 panVertices[vertices-7] = render_view.backingHeight-pan.pos_y;
				 
				 panVertices[vertices-6] = pan.pos_x+pan.size.x;
				 panVertices[vertices-5] = render_view.backingHeight-pan.pos_y;
				 
				 panVertices[vertices-4] = pan.pos_x+pan.size.x;
				 panVertices[vertices-3] = render_view.backingHeight-pan.pos_y-pan.size.y;
				 
				 panVertices[vertices-2] = pan.pos_x;
				 panVertices[vertices-1] = render_view.backingHeight-pan.pos_y;
			 } else {
				 //vertices
				 vert_temppos_x = -pan.size.x*0.5f;
				 vert_temppos_y = -pan.size.y*0.5f;
				 vert_dist = sqrt(vert_temppos_x*vert_temppos_x+vert_temppos_y*vert_temppos_y);
				 vert_ang = atan2(vert_temppos_y,vert_temppos_x);
				 vert_temppos_x = cos(vert_ang+pan.rotation)*vert_dist+pan.pos_x+pan.size.x*0.5;
				 vert_temppos_y = sin(vert_ang+pan.rotation)*vert_dist+render_view.backingHeight-pan.pos_y-pan.size.y*0.5;
				 
				 panVertices[vertices-12] = vert_temppos_x;
				 panVertices[vertices-11] = vert_temppos_y;
				 
				 vert_temppos_x = pan.size.x*0.5f;
				 vert_temppos_y = -pan.size.y*0.5f;
				 vert_ang = atan2(vert_temppos_y,vert_temppos_x);
				 vert_temppos_x = cos(vert_ang+pan.rotation)*vert_dist+pan.pos_x+pan.size.x*0.5;
				 vert_temppos_y = sin(vert_ang+pan.rotation)*vert_dist+render_view.backingHeight-pan.pos_y-pan.size.y*0.5;
				 
				 panVertices[vertices-10] = vert_temppos_x;
				 panVertices[vertices-9] = vert_temppos_y;
				 
				 
				 vert_temppos_x = -pan.size.x*0.5f;
				 vert_temppos_y = pan.size.y*0.5f;
				 vert_ang = atan2(vert_temppos_y,vert_temppos_x);
				 vert_temppos_x = cos(vert_ang+pan.rotation)*vert_dist+pan.pos_x+pan.size.x*0.5;
				 vert_temppos_y = sin(vert_ang+pan.rotation)*vert_dist+render_view.backingHeight-pan.pos_y-pan.size.y*0.5;
				 
				 panVertices[vertices-8] = vert_temppos_x;
				 panVertices[vertices-7] = vert_temppos_y;
				 
				 
				 vert_temppos_x = pan.size.x*0.5f;
				 vert_temppos_y = pan.size.y*0.5f;
				 vert_ang = atan2(vert_temppos_y,vert_temppos_x);
				 vert_temppos_x = cos(vert_ang+pan.rotation)*vert_dist+pan.pos_x+pan.size.x*0.5;
				 vert_temppos_y = sin(vert_ang+pan.rotation)*vert_dist+render_view.backingHeight-pan.pos_y-pan.size.y*0.5;
				 
				 panVertices[vertices-6] = vert_temppos_x;
				 panVertices[vertices-5] = vert_temppos_y;
				 
				 
				 vert_temppos_x = pan.size.x*0.5f;
				 vert_temppos_y = -pan.size.y*0.5f;
				 vert_ang = atan2(vert_temppos_y,vert_temppos_x);
				 vert_temppos_x = cos(vert_ang+pan.rotation)*vert_dist+pan.pos_x+pan.size.x*0.5;
				 vert_temppos_y = sin(vert_ang+pan.rotation)*vert_dist+render_view.backingHeight-pan.pos_y-pan.size.y*0.5;
				 
				 panVertices[vertices-4] = vert_temppos_x;
				 panVertices[vertices-3] = vert_temppos_y;
				 
				 
				 vert_temppos_x = -pan.size.x*0.5f;
				 vert_temppos_y = pan.size.y*0.5f;
				 vert_ang = atan2(vert_temppos_y,vert_temppos_x);
				 vert_temppos_x = cos(vert_ang+pan.rotation)*vert_dist+pan.pos_x+pan.size.x*0.5;
				 vert_temppos_y = sin(vert_ang+pan.rotation)*vert_dist+render_view.backingHeight-pan.pos_y-pan.size.y*0.5;
				 
				 panVertices[vertices-2] = vert_temppos_x;
				 panVertices[vertices-1] = vert_temppos_y;  
			 }
		}
		
		free(visPans);
		
		drawCalls += 1;
		
		tempId = lastPan.userData;
		
		if([tempId isKindOfClass:[VD_ParticleEmitter class]])
		{
			tempEmitter = tempId;
			
			if(tempEmitter.bright == YES)
				glBlendFunc(GL_SRC_ALPHA, GL_DST_ALPHA);
		}
		
		glBindTexture(GL_TEXTURE_2D, texMan.texLib[lastPan.texture]);
		
		glLoadIdentity();
		glVertexPointer(2, GL_FLOAT, 0, panVertices);
		glTexCoordPointer(2, GL_FLOAT, 0, panTexCoords);
		
		glDrawArrays(GL_TRIANGLES, 0, vertices/2);
		
		free(panVertices);
		free(panTexCoords);
		
		if([tempId isKindOfClass:[VD_ParticleEmitter class]])
		{
			if(tempEmitter.bright == YES)
				glBlendFunc(GL_SRC_ALPHA, GL_ONE_MINUS_SRC_ALPHA);
		}
	
	}
	panelCamera_x = 0;
	panelCamera_y = 0;
	
	render_view.visiblePolygons += polyCount;
	render_view.drawCalls += drawCalls;
	render_view.verticesCount += drawVertices;
}

- (VD_Panel *)isTouchInPanel:(CGPoint)pos {
	VD_Panel *temp_pan;
	
	for(int i=(aktPanels-1); i>=0; i--)
	{
		temp_pan = panels[i];
		if([temp_pan posInMe:pos])
		{
			if(touchSelectMode == kDefaultMode)
			{
				touchWasIn.x = pos.x-temp_pan.pos_x;
				touchWasIn.y = pos.y-temp_pan.pos_x;
			
				return temp_pan;
			} 
			else if(touchSelectMode == kAlphaReckoningMode)
			{
				VD_Image *tempImage = [[VD_Image alloc] init];
				[tempImage lockBitmap:texMan.loadedTextures[temp_pan.texture]];
				
				VD_Color *tempCol = [tempImage getBitmapColor:CGPointMake(pos.x-temp_pan.pos_x, pos.y-temp_pan.pos_y)];
				if(tempCol.alpha > 255-alphaThreshold)
				{
					touchWasIn.x = pos.x-temp_pan.pos_x;
					touchWasIn.y = pos.y-temp_pan.pos_x;
					
					return temp_pan;
				}
				
				[tempCol release];
				[tempImage release];
			}
		}
	}
	return NULL;
}

- (VD_Panel *)isTouchInPanel:(CGPoint)pos andInLayer:(int)layer {
	VD_Panel *temp_pan;
	
	for(int i=(aktPanels-1); i>=0; i--)
	{
		temp_pan = panels[i];
		if(temp_pan.layer == layer)
		{
			if([temp_pan posInMe:pos])
			{
				if(touchSelectMode == kDefaultMode)
				{
					touchWasIn.x = pos.x-temp_pan.pos_x;
					touchWasIn.y = pos.y-temp_pan.pos_x;
					
					return temp_pan;
				} 
				else if(touchSelectMode == kAlphaReckoningMode)
				{
					VD_Image *tempImage = [[VD_Image alloc] init];
					[tempImage lockBitmap:texMan.loadedTextures[temp_pan.texture]];
					
					VD_Color *tempCol = [tempImage getBitmapColor:CGPointMake(pos.x-temp_pan.pos_x, pos.y-temp_pan.pos_y)];
					if(tempCol.alpha > 255-alphaThreshold)
					{
						touchWasIn.x = pos.x-temp_pan.pos_x;
						touchWasIn.y = pos.y-temp_pan.pos_x;
						
						return temp_pan;
					}
					
					[tempCol release];
					[tempImage release];
				}
				
			}
		}
	}
	return NULL;
}

- (VD_Panel *)nextPanel:(VD_Panel *)pan {
	if(pan == NULL && aktPanels > 0)
	{
		return panels[0];
	}
	
	for(int i=0; i<aktPanels; i++)
	{
		if(pan == panels[i])
		{
			if(i+1 < aktPanels)
			{
				return panels[i+1];
			} else {
				return NULL;
			}
		}
	}
	
	return NULL;
}

- (void)deleteParticleEmitter:(VD_ParticleEmitter *)emit {
	int i;
	for(i=0; i<emitterCount; i++)
	{
		if(emit == emitter[i])
		{
			[emit release];
			
			for(int j=i; j<emitterCount-1; j++)
			{
				emitter[j] = emitter[j+1];
			}
			
			emitterCount -= 1;
			emitter = (VD_ParticleEmitter **)realloc(emitter, emitterCount*sizeof(VD_ParticleEmitter *));
			
			break;
		}
	}
}

- (void)deletePanel:(VD_Panel *)pan {
	//Checke ob das Objekt übehaupt durch den panmanager gehandled wird
	int i;
	for(i=0; i<aktPanels; i++)
	{
		if(pan == panels[i])
		{
			[pan release];
			
			for(int j=i; j<aktPanels-1; j++)
			{
				panels[j] = panels[j+1];
			}
			
			aktPanels -= 1;
			panels = (VD_Panel **)realloc(panels, aktPanels*sizeof(VD_Panel *));
			
			break;
		}
	}
}

- (void)deleteAnimation:(VD_Animation *)ani {
	//Checke ob das Objekt übehaupt durch den panmanager gehandled wird
	int i;
	for(i=0; i<aktAnimations; i++)
	{
		if(ani == animations[i])
		{
			animations[i] = NULL;
			[ani release];
			
			for(int j=i; j<aktAnimations; j++)
			{
				animations[j] = animations[j+1];
			}
			
			aktAnimations -= 1;
			animations = (VD_Animation **)realloc(animations, aktAnimations*sizeof(VD_Animation *));
			
			break;
		}
	}
}

- (VD_Panel *)p_trace:(CGPoint)start :(CGPoint)end {
	VD_Panel *pan;
	
	float temp_x = end.x - start.x;
	float temp_y = end.y - start.y;
	
	float distance = sqrt(temp_x*temp_x	+ temp_y*temp_y);
	
	temp_x = temp_x/distance;
	temp_y = temp_y/distance;
	
	float x = start.x;
	float y = start.y;
	
	while(x < end.x && y < end.y)
	{
		x += temp_x;
		y += temp_y;
		
		pan	= [self isTouchInPanel:CGPointMake(x, y)];
		if(pan != NULL)
			break;
	}
	
	return pan;
}

@end
